// Copyright (c) 2010 god6or@gmail.com under MIT license.

package ul.comm

import collection.mutable.{ArrayBuffer, HashMap}
import ul.math.crc.{CRC8_Dallas, CRC16_Dallas}


object OneWire extends OneWire {
    object cmds { // command codes
        val SEARCH_ROM      = 240.toByte
        val SEARCH_ALARM    = 236.toByte
        val RD_ROM          = 51.toByte
        val MATCH_ROM       = 85.toByte
        val MATCH_ROM_OD    = 105.toByte
        val SKIP_ROM        = 204.toByte
        val SKIP_ROM_OD     = 60.toByte
        val CONV_T_1822     = 68.toByte
        val WR_SP_1822      = 78.toByte
        val RD_SP_1822      = 190.toByte
        val CP_SP_1822      = 72.toByte
        val RECALL_E2_1822  = 184.toByte
        val RD_POWER_1822   = 180.toByte
        val WR_SP_1995      = 15.toByte
        val RD_SP_1995      = 170.toByte
        val CP_SP_1995      = 85.toByte
        val RD_MEM_1995     = 240.toByte
        val RD_T_1821       = 170.toByte
        val START_CONV_1821 = 238.toByte
        val STOP_CONV_1821  = 34.toByte
        val WR_TH_1821      = 1.toByte
        val WR_TL_1821      = 2.toByte
        val RD_TH_1821      = 161.toByte
        val RD_TL_1821      = 162.toByte
        val WR_STATUS_1821  = 12.toByte
        val RD_STATUS_1821  = 172.toByte
        val RD_CNT_1821     = 160.toByte
        val LD_CNT_1821     = 65.toByte
        val RESUME_CMD_2432 = 165.toByte
        val RD_MEM_2450     = 170.toByte
        val WR_MEM_2450     = 85.toByte
    }

    object types { // devices family codes
        val DS1990A, DS1990R, DS2401, DS2411 = 1
        val DS1991 = 2
        val DS1994, DS2404 = 4
        val DS24051 = 5
        val DS1993 = 6
        val DS1992 = 8
        val DS1982, DS2502 = 9
        val DS1995 = 10
        val DS1985, DS2505 = 11
        val DS1996 = 12
        val DS1986, DS2506 = 15
        val DS1920, DS18S20 = 16
        val DS2406, DS24071 = 18
        val DS1971, DS2430A1 = 20
        val DS1963L = 26
        val DS28E04_100 = 28
        val DS24231 = 29
        val DS24091 = 31
        val DS2450 = 32
        val DS1921G, DS1921H, DS1921Z = 33
        val DS1822 = 34
        val DS1973, DS2433 = 35
        val DS1904, DS2415 = 36
        val DS2417 = 39
        val DS18B20 = 40
        val DS2408 = 41
        val DS28901 = 44
        val DS1972, DS2431 = 45
        val DS1977 = 55
        val DS2413 = 58
        val DS1922L, DS1922T, DS1923, DS2422 = 65
        val DS28EA00 = 66
        val DS28EC20 = 67
    }

    val names = HashMap[Int, List[String]](
        1 -> List("DS1990A", "DS1990R", "DS2401", "DS2411"),
        2 -> List("DS1991"),
        4 -> List("DS1994", "DS2404"),
        5 -> List("DS24051"),
        6 -> List("DS1993"),
        8 -> List("DS1992"),
        9 -> List("DS1982", "DS2502"),
        10 -> List("DS1995"),
        11 -> List("DS1985", "DS2505"),
        12 -> List("DS1996"),
        15 -> List("DS1986", "DS2506"),
        16 -> List("DS1920", "DS18S20"),
        18 -> List("DS2406", "DS24071"),
        20 -> List("DS1971", "DS2430A1"),
        26 -> List("DS1963L"),
        28 -> List("DS28E04_100"),
        29 -> List("DS24231"),
        31 -> List("DS24091"),
        32 -> List("DS2450"),
        33 -> List("DS1921G", "DS1921H", "DS1921Z"),
        34 -> List("DS1822"),
        35 -> List("DS1973", "DS2433"),
        36 -> List("DS1904", "DS2415"),
        39 -> List("DS2417"),
        40 -> List("DS18B20"),
        41 -> List("DS2408"),
        44 -> List("DS28901"),
        45 -> List("DS1972", "DS2431"),
        55 -> List("DS1977"),
        58 -> List("DS2413"),
        65 -> List("DS1922L", "DS1922T", "DS1923", "DS2422"),
        66 -> List("DS28EA00"),
        67 -> List("DS28EC20")
    )
}

class OneWire {

    val tName   = "ow"        // interface type
    val name    = "one wire"  // interface name
    val descr   = "basic one wire interface"  // interface description
    
    var opened = false

    var channels  = 1 // number of 1-wire channels, supported by interface
    var __channel = 0 // current communication channel (0..nchn-1)

    var retriesRes         = 3  // number of bus reset retries
    var retriesSearch      = 3  // number of search attempt retries
    var retriesSearchNode  = 3  // number of search node read retries
    var retriesSearchPause = 10 // search retry pause, ms
    var retriesRead        = 3  // number of read retries
    var retriesReadPause   = 10 // read retry pause, ms
    
    val crc8 = new CRC8_Dallas
    val crc16 = new CRC16_Dallas

    /// select 1-wire channel
    def channelSet(chn: Int) = {
        if ((chn >= 0) && (chn <= this.channels)) this.channel = chn
    }
    
    /// switch on/off parasite power
    def parasite(on:Boolean) = {
    }
    
    def calcCRC(data:Seq[Byte], crc16:Boolean, start: Int, end: Int): Int = {
        if (!crc16) this.crc8.reset.upd(data, start, end).get
        else this.crc16.reset.upd(data, start, end).get
    }
    def calcCRC(data:Seq[Byte], crc16:Boolean): Int = {
        calcCRC( data, crc16, 0, data.length-1 )
    }
    def calcCRC(data:Seq[Byte]): Int = {
        calcCRC( data, false, 0, data.length-1 )
    }

    /// checks CRC8/16 of received data buffer
    ///  data - array of bytes
    ///  crc16==true to check CRC16 instead of CRC8
    def checkCRC(data:Seq[Byte], crc16:Boolean=false): Boolean = {
        var result = false
        if (data.length > 2) {
            if (!crc16) result = (calcCRC(data, crc16, 0, data.length-2) == (data(data.length-1) & 0xFF))
            else result = (calcCRC(data, crc16, 0, data.length-3) == ((data(data.length-2) & 0xFF) + (data(data.length-1) & 0xFF) * 256))
        }
        result
    }

    /// adds CRC8/16 to data chunk
    ///   data - array of bytes
    ///   crc16==true to add CRC16
    def addCRC(data:Array[Byte], crc16:Boolean=false): Array[Byte] = {
        if (!crc16) { data ++ Array[Byte]( this.crc8.reset.upd( data ).get.toByte ) }
        else {
            val crc = this.crc16.reset.upd(data).get
            data ++ Array[Byte]( (crc % 256).toByte, (crc / 256).toByte )
        }
    }
    
    /// parses 64-bit serial code (checks CRC, extracts family and serial number).
    ///   returns: tuple with family code and sn - serial number.
    ///   on error returns nulls.
    def serialParse(ser:Seq[Byte]): Option[(Int, Long, List[Byte])] = {
        if ((ser.length == 8) && checkCRC(ser)) Option((
            ser(0) & 0xFF,
            (ser(1) & 0xFFL) +
            ((ser(2) & 0xFFL) << 8) +
            ((ser(3) & 0xFFL) << 16) +
            ((ser(4) & 0xFFL) << 24) +
            ((ser(5) & 0xFFL) << 32) +
            ((ser(6) & 0xFFL) << 40),
            ser.toList
        )) else None
    }
    
    /// generate serial code packet (type + serial number + crc) from type and serial
    def serialGen( t: Int, sn: Long ): List[Byte] = {
        val ser = List( t.toByte,
            (sn & 0xFF).toByte,
            ((sn >>> 8) & 0xFF).toByte,
            ((sn >>> 16) & 0xFF).toByte,
            ((sn >>> 24) & 0xFF).toByte,
            ((sn >>> 32) & 0xFF).toByte,
            ((sn >>> 40) & 0xFF).toByte
        )
        ser ++ List(calcCRC(ser).toByte)
    }

    /// convert temperature registers value to Celsius.
    ///   tl,th - values of low and high temperature registers.
    ///   nbits - number of temperature bits (9..15, default is 12),
    ///   cntpc, cntrem - values of COUNT_PER_C and COUNT_REMAIN registers used to 
    ///       enhance measurement resolution of 9-bit sensors.
    ///       If cntpc == cntrem == null, resolution is not enhanced.
    def convT2C(tl:Byte, th:Byte, nbits:Int=12, cntRem:Option[Int]=None, cntPC:Option[Int]=None): Double = {
        var t = (th & 0xFF) * 256 + (tl & 0xFF) // th,tl as separate registers
        var sign = 1.0
        if (t > 32767) { t = 65536 - t; sign = -1.0 }
        var tc:Double = sign * t * math.pow(2, 16 - nbits) * 0.00390625
        if ( nbits == 9 && !cntPC.isEmpty && !cntRem.isEmpty )
            tc = math.floor(tc) - 0.25 + ((cntPC.get - cntRem.get) / cntPC.get.toDouble)
        return tc
    }
    // convert temperature in Celsius to 16-bit Int using sensor resolution (default is 12 bits).
    def convC2T( t: Double, resolution: Int = 12 ): Int = {
        var temp = math.abs(t)
        var reg: Int = math.round(temp / (math.pow(2, 16 - resolution) * 0.00390625)).toInt
        if (t < 0) reg = 65536 - reg
        reg
    }
    /// convert Celcius to Fahrenheit
    def convC2F(t: Double) {
        return t * (9.0 / 5.0 + 32.0)
    }

    /// convert sequence of bytes to sequence of transmittable bits (LSB)
    def bytesToBits( bytes: Seq[Byte] ): List[Byte] = {
        val bits = new ArrayBuffer[Byte]
        for (b <- bytes) {
            var bb = b & 0xFF
            for (c <- 0 to 7) {
                if ((bb % 2) == 0) bits += 0.toByte else bits += (-1).toByte
                bb >>>= 1
            }
        }
        bits.toList
    }
    
    /// collect sequence of received bits into bytes (LSB)
    def bitsToBytes( bits: Seq[Byte] ): List[Byte] = {
        val bytes = new ArrayBuffer[Byte]
        if ((bits.length % 8) == 0) {
            var bc = 0
            while (bc < bits.length) {
                var b = 0
                for (c <- 0 to 7) {
                    b >>>= 1
                    if (bits(bc) == -1) b += 128
                    bc += 1
                }
                bytes += b.toByte
            }
        }
        bytes.toList
    }
    
    /// get/set current channel
    def channel: Int = __channel
    def channel_=(c:Int) = __channel = c
    
    /// get/set state of parasite power ( using RTS level - passive for power OFF, active for ON )
    def parasite: Boolean = false
    def parasite_=(on:Boolean) = {}

    /// resets bus, returns true if ACK received
    def reset: Boolean = false

    /// performs bus search, returns list of device info tuples
    def search(cmd: Int = 0xF0): List[(Int, Long, List[Byte])] = Nil

    def close = {
        opened = false
    }

    /// Perform packet transaction. Reset bus, write wrBytes, read nrdBytes bytes, check CRC of received packet, return Nil if errors
    def wrRdPacket(wrBytes: List[Byte], nrdBytes: Int, chkCRC: Boolean=false, crc16: Boolean=false): List[Byte] = Nil

/*    -- parses data received from device.
    --   sc - device serial code (string or parsed),
    --   data - string of data bytes received from device,
    --   checkCRC==true if data contains CRC and needs to be checked.
    -- returns table with parsed parameters.
    parseData = function(self, sc, data)
        if type(sc) == 'string' then sc = self:parseSC(sc) end
        if sc and type(data) == 'string' then

            if (sc.fam == self.devs.DS18B20) and (#data == 9) and self:checkCRC(data) then -- DS18B20 - parse scratchpad
                local conf = data:byte(5) -- configuration register
                local bits = self.mod(self.floor(conf / 32), 4) + 9 -- resolution, bits
                local t = data:byte(2) * 256 + data:byte(1) -- temperature (register)
                local tc = self:convT2C(t, nil, bits) -- temperature, Celsius
                local tf = self:convC2F(tc)
                local ta = data:byte(3) * 256 + data:byte(4) -- alarm temperature (register)
                local tac = self:convT2C(ta, nil, bits) -- alarm temperature (celsius)
                return {conf=conf, bits=bits, t=t, tc=tc, tf=tf, ta=ta, tac=tac} -- return parsed data table

            elseif (sc.fam == self.devs.DS1990A) then -- DS1990A
                return sc
            end

        end
    end,

    -- reads data from device.
    --   sc - device serial code (string or parsed).
    -- returns table with parsed parameters.
    readDev = function(self, sc)
    end,

*/}



/// 1-wire communication through RS232 port
class OneWireSerial( val port:Serial ) extends OneWire {
    
    var timeoutRes = 300  // reset delay, milliseconds
    var timeoutRX  = 1000 // RX timeout, in 100-microsecond increments
    var waitTX     = 30   // Pause after data TX, milliseconds
    var writeChunk = 256  // size of write chunk
    
    opened = true
    portSetup( false )
    channels = 2 // number of communication channels
    channel = 0
    parasite = false

    /// prepare communication port to Reset (true) or Communication (false)
    def portSetup( reset: Boolean = false ) = {
        port.flush
        if (reset) port.setup( 9600, 8, 1.0, "n" ) else port.setup( 115200, 8, 1.0, "n" )
        port.timeoutRX = 100
    }

    /// get/set current 1-wire channel ( using DTR level - passive for channel 0, active - channel 1 )
    override def channel: Int = if (port.DTR) 1 else 0
    override def channel_=(c:Int) = c match {
        case 0 => port.DTR = false
        case 1 => port.DTR = true
        case _ =>
    }
    
    /// get/set state of parasite power ( using RTS level - passive for power OFF, active for ON )
    override def parasite: Boolean = port.RTS
    override def parasite_=(on:Boolean) = {
        port.RTS = on
    }


    /// resets 1-wire bus, returns reset result (true if acknowledge pulse received)
    override def reset: Boolean = {
        var result  = false      // operation result
        var retries = retriesRes // number of reset retries
        val par     = parasite // remember parasite power state
        parasite    = false      // switch off parasite power
        portSetup( true )        // prepare port for reset pulse
        while (!result && (retries > 0)) {
            port.write(List(240.toByte)) // send reset
            if (port.availWait(1, timeoutRes)) {
                val dataRX = port.read(-1)
                if (dataRX.length > 0) {
                    val resp = dataRX( dataRX.length - 1) & 0xFF
                    if ((resp == 224)||(resp == 192)||(resp == 144)||(resp == 128)||(resp == 16))
                        result = true
                }
            }
            if (!result) retries -= 1
            //if (!result) println("Reset retries: " + retries)
        }
        portSetup(false) // restore port speed
        parasite = par   // restore parasite power state
        result
    }

    // write sequence of bits (0/1) and return array of received bits (empty if none received)
    def wrRdBits(wrBits: Seq[Byte]): List[Byte] = {
        val rdBits = new ArrayBuffer[Byte]
        if (wrBits.length > 0) {
            port.flushRX
            var bitCnt = 0
            while (bitCnt < wrBits.length) {
                var bitsToWr = wrBits.length - bitCnt
                if (bitsToWr > writeChunk) bitsToWr = writeChunk
                port.write( for (c <- bitCnt to (bitCnt + bitsToWr - 1)) yield if (wrBits(c) == 0) 0.toByte else (-1).toByte )
                if (port.availWait(bitsToWr, timeoutRX)) {
                    rdBits ++= (for (b <- port.read(-1)) yield if (b == -1) (-1).toByte else 0.toByte)
                } else {
                    return Nil
                }
                port.flushRX
                bitCnt += bitsToWr
            }
        }
        return rdBits.toList
    }

    // write sequence of bytes and return array of received bytes (empty if none received)
    def wrRdBytes(wrBytes: Seq[Byte]): List[Byte] = {
        val rdBytes = new ArrayBuffer[Byte]
        if (wrBytes.length > 0) {
            val wrBits = new ArrayBuffer[Byte]
            for (b <- wrBytes) { // generate TX bits
                var bb = b & 0xFF
                for (bc <- 0 to 7) {
                    if ((bb % 2) == 0) wrBits += 0.toByte else wrBits += (-1).toByte;
                    bb >>>= 1
                }
            }
            val rdBits = wrRdBits( wrBits ) // send TX and receive RX bits
            if (rdBits.length == wrBits.length) { // collect RX bits into bytes
                var bitCnt = 0
                while (bitCnt < rdBits.length) {
                    var rdByte = 0
                    for (bc <- 0 to 7) {
                        rdByte >>>= 1
                        if (rdBits(bitCnt) == -1) rdByte += 128;
                        bitCnt += 1
                    }
                    rdBytes += rdByte.toByte
                }
            }
        }
        return rdBytes.toList
    }
    
    /// resets bus, sends packet with wrBytes, reads rdBytes of response, ckecks CRC8/16 of received packet
    override def wrRdPacket(wrBytes: List[Byte], nrdBytes: Int, chkCRC: Boolean=false, crc16: Boolean=false): List[Byte] = {
        var retries = retriesRead
        var result: List[Byte] = Nil;
        while ((result == Nil) && (retries > 0)) {
            if (this.reset) {
                val rdBytes = wrRdBytes( wrBytes ++ (for (i <- 1 to nrdBytes) yield 255.toByte) )
                if ((nrdBytes > 0)&&(rdBytes.length == (wrBytes.length + nrdBytes))) {
                    // parse received bytes
                    val rdPacket = rdBytes.slice( wrBytes.length, wrBytes.length + nrdBytes )
                    if (!chkCRC) result = rdPacket
                        else if (checkCRC(rdPacket, crc16)) result = rdPacket
                }
            }
            if (result == Nil) retries -= 1
        }
        return result
    }

    /// Search devices on bus.
    /// cmd - search command (default is SEARCH_ROM).
    /// Returns list of connected devices in format List((Family_Code, Serial_Number),...).
    override def search(cmd: Int = 0xF0): List[(Int, Long, List[Byte])] = {
        var retries = retriesSearch
        var result = new ArrayBuffer[(Int,Long,List[Byte])]
        var disreps: ArrayBuffer[ ArrayBuffer[Byte] ] = null
        val bits = new ArrayBuffer[Byte]
        val ndisreps = new ArrayBuffer[ ArrayBuffer[Byte] ]
        val wb = new ArrayBuffer[Byte]
        val curbits = new ArrayBuffer[Byte]
        val disbits = new ArrayBuffer[Byte]
        val s = new ArrayBuffer[Byte]

        while ((result.length == 0)&&(retries > 0)) {
            while ((disreps == null)||(disreps.length > 0)) {

                if (disreps == null) disreps = new ArrayBuffer[ ArrayBuffer[Byte] ]

                bits.clear
                // use last disrepancy bits from array
                if (disreps.length > 0) bits ++= disreps.remove( disreps.length - 1)

                // node read retries counter and read success flag
                var (nretrs, nok) = (retriesSearchNode, false)
                
                while (!nok && (nretrs > 0)) { // do search for current node
                    ndisreps.clear // disrepancies array found for current node
                    
                    if (reset) { // proceed if presence pulse detected
                        //portSetup(false)
                        //println("reset OK")
                        wb.clear
                        wb ++= bytesToBits( List(cmd.toByte) ) // search ROM command
                        for (bit <- bits) {
                            wb += (-1).toByte
                            wb += (-1).toByte
                            wb += bit
                        }
                        //println("sending bits: " + wb)
                        wrRdBits(wb) // write Search ROM command and bits of last disrepancy

                        curbits.clear
                        for (bit <- bits) curbits += bit
                        
                        var errRead = false
                        for (bc <- curbits.length+1 to 64 if !errRead) { // read rest of bits from current disrepancy
                            //port.flush
                            val rb = wrRdBits( List((-1).toByte, (-1).toByte ) )
                            //Thread.sleep(10)
                            if (rb.length == 2) {
                                // get next bit and negated bit
                                //println("Bit: " + rb(0) + ", nbit: " + rb(1))
                                val (bit, nbit) = (rb(0), rb(1))
                                if (bit != nbit) { // no disrepancy
                                    curbits += bit
                                    wrRdBits( List(bit) )
                                } else if (bit == 0) { // disrepancy found - add to array
                                    disbits.clear
                                    for (b <- curbits) disbits += b // copy curent bits
                                    disbits += (-1).toByte // proceed with '1' direction next time
                                    ndisreps += (new ArrayBuffer[Byte] ++= disbits) // push to disrepancies array
                                    curbits += 0.toByte // proceed with '0' direction
                                    wrRdBits( List(0.toByte) )
                                } else { // error - stop search
                                    curbits.clear
                                    errRead = true
                                }
                            }
                        }

                        // search successful - add new node to array
                        s.clear;
                        if ((curbits.length > 0)&&((curbits.length % 8) == 0)) s ++= bitsToBytes(curbits)
                        
                        //println("Serial: " + s)
                        // check CRC and append new node to result if success
                        val sc = serialParse(s)
                        if (!sc.isEmpty) { // CRC ok - add node to results array
                            result += sc.get
                            // copy disrepancies for current node to global array
                            disreps ++= ndisreps
                            nok = true // proceed to next node
                        }
                    }
                    if (!nok) nretrs -= 1
                    //if (!nok) println("Node search retries: " + nretrs)
                }
            }

            // nothing found - retry search
            if (result.length == 0) {
                retries -= 1
                //println("Search retries: " + retries)
                if (retries > 0) {
                    Thread.sleep( retriesSearchPause )
                    disreps = null
                } else disreps.clear
            }
        }
        
        result.toList
    }


    override def close = {
        //port.close
        opened = false
    }
}


/// 1-wire communication through DS2480 connected to RS232 port
//class OneWireSerialDS2480 extends OneWire {
//}



class OneWireDevice( val typeCode: Int, val serialNumber: Long ) {
    val typeName = if (OneWire.names.contains(typeCode)) OneWire.names(typeCode)(0) else ""; // device type name
    val serialNumberHex = format("%X", serialNumber) // hexadecimal representation of serial number
    var serialPacket: List[Byte] = OneWire.serialGen( typeCode, serialNumber )
    var dataIsGood: Boolean = false // last read data read was good
    
    // tests equivalence to other device
    def equalsTo( other: OneWireDevice ): Boolean = ((typeCode == other.typeCode)&&(serialNumber == other.serialNumber))
    
    // reads and parses device contents
    def rdData( ow: OneWire ): Boolean = false
    // writes current device data
    def wrData( ow: OneWire ): Boolean = false
    // sends start conversion code
    def startConversion( ow: OneWire ): Boolean = false
}
class OneWireThermometer( override val typeCode: Int, override val serialNumber: Long )
    extends OneWireDevice( typeCode, serialNumber ) {
    var temperature: Double = Double.NaN
    var resolutionBits: Int = 12 // temperature resolution, bits
    
    // returns sensor resolution, degrees
    def resolution = (1.0 / math.pow(2, resolutionBits - 8))
}
class OneWire18B20( override val serialNumber: Long ) extends OneWireThermometer( OneWire.types.DS18B20, serialNumber ) {
    var alarm: Double = Double.NaN
    
    override def rdData( ow: OneWire ): Boolean = {
        var success = false
        // read scratchpad
        val scratchpad = ow.wrRdPacket(
            List(OneWire.cmds.MATCH_ROM) ++ serialPacket ++ List(OneWire.cmds.RD_SP_1822),
            9, true, false
        )
        if (scratchpad.length == 9) { // parse data
            resolutionBits = ((scratchpad(4) & 0x60) >>> 5) + 9
            temperature = OneWire.convT2C(scratchpad(0), scratchpad(1), resolutionBits)
            alarm = OneWire.convT2C(scratchpad(3), scratchpad(2), resolutionBits)
            success = true
        }
        dataIsGood = success
        success
    }
    override def wrData( ow: OneWire ): Boolean = {
        var success = false
        if (!alarm.isNaN) {
            val scratchpad = new Array[Byte](3)
            val alarmreg = OneWire.convC2T( alarm, resolutionBits )
            scratchpad(0) = (alarmreg % 256).toByte
            scratchpad(1) = (alarmreg >>> 8).toByte
            scratchpad(3) = ((resolutionBits - 9) << 5).toByte
            ow.wrRdPacket(
                List(OneWire.cmds.MATCH_ROM) ++ serialPacket ++ List(OneWire.cmds.WR_SP_1822) ++ scratchpad,
                0, false, false
            )
            success = true
        }
        success
    }
    override def startConversion( ow: OneWire ): Boolean = {
        var success = false
        ow.wrRdPacket( List(OneWire.cmds.MATCH_ROM) ++ serialPacket ++ List(OneWire.cmds.CONV_T_1822), 0, false, false )
        success = true
        success
    }
}
class OneWire18S20( override val serialNumber: Long ) extends OneWireThermometer( OneWire.types.DS18S20, serialNumber ) {
    var alarm: Double = Double.NaN
    resolutionBits = 9
    
    override def rdData( ow: OneWire ): Boolean = {
        var success = false
        // read scratchpad
        val scratchpad = ow.wrRdPacket(
            List(OneWire.cmds.MATCH_ROM) ++ serialPacket ++ List(OneWire.cmds.RD_SP_1822),
            9, true, false
        )
        if (scratchpad.length == 9) { // parse data
            temperature = OneWire.convT2C(scratchpad(0), scratchpad(1), resolutionBits, Some(scratchpad(6) & 0xFF), Some(scratchpad(7) & 0xFF))
            alarm = OneWire.convT2C(scratchpad(3), scratchpad(2), resolutionBits)
            success = true
        }
        dataIsGood = success
        success
    }
    override def wrData( ow: OneWire ): Boolean = {
        var success = false
        if (!alarm.isNaN) {
            val scratchpad = new Array[Byte](2)
            val alarmreg = OneWire.convC2T( alarm, resolutionBits )
            scratchpad(0) = (alarmreg % 256).toByte
            scratchpad(1) = (alarmreg >>> 8).toByte
            ow.wrRdPacket(
                List(OneWire.cmds.MATCH_ROM) ++ serialPacket ++ List(OneWire.cmds.WR_SP_1822) ++ scratchpad,
                0, false, false
            )
            success = true
        }
        success
    }
    override def startConversion( ow: OneWire ): Boolean = {
        var success = false
        ow.wrRdPacket( List(OneWire.cmds.MATCH_ROM) ++ serialPacket ++ List(OneWire.cmds.CONV_T_1822), 0, false, false )
        success = true
        success
    }
}

object TestOWSerial {
    def main( args: Array[String] ) = {
/*        val p = Serial(0)
        val ow = new OneWireSerial( p )
        println
        println("Reset: " + ow.reset)
        val sp = ow.wrRdPacket( List(0xCC.toByte, 0xBE.toByte), 9 )
        println("DS18B20: " + sp)
        println("CRC8: " + ow.calcCRC(sp, false, 0, sp.length-2) + " " + (sp(sp.length-1) & 0xFF) + " " + ow.checkCRC(sp, false))
        val ows = ow.search()
        println("Search: " + ows)
        for (o <- ows) {
            val (t,sn,sb) = o
            println("SN: " + ow.serialGen(t,sn))
        }
*/    }
}

